import os
import re

from django.db import models
from django.core.management.base import copy_helper
from django.core.management.color import color_style
from django.utils.importlib import import_module
from django.conf import settings

class Application (models.Model):
    name = models.CharField(max_length=200)
    is_installed = models.BooleanField()

    def __unicode__(self):
        return self.name

    def start_app(self):
        directory = settings.PROJECT_DIR
        project_name = os.path.basename(directory)
        app_name = self.name
        if app_name == project_name:
            print "You cannot create an app with the same name (%r) as your project." % (app_name,)

        # Check that the app_name cannot be imported.
        try:
            import_module(app_name)
        except ImportError:
            pass
        else:
            print "%r conflicts with the name of an existing Python module and cannot be used as an app name. Please try another name." % (app_name,)

        copy_helper(color_style(), 'app', app_name, directory, project_name)
 
    def generate_models(self, first_time=False):
        directory = settings.PROJECT_DIR
        models_file_path = "%s/%s/models.py" % (directory, self.name)
        if not first_time:
            old_models = open(models_file_path, 'r')
            model_tags = {"custom_imports":""}
            is_custom_import = False
            is_custom_code = False
            for line in old_models:
                if "<-- User custom imports after this tag. Do not remove!!!" in line:
                    is_custom_import = True
                    continue
                if "# End of user custom imports. Do not remove this tag!!! -->" in line:
                    is_custom_import = False
                    continue
                if is_custom_import:
                    model_tags["custom_imports"] += "%s" % (line,)
                d = re.search('(?<=@)\w+@', line)
                if d and not is_custom_code:
                    tag_name = "@%s" % (d.group(0),)
                    model_tags[tag_name] = ""
                    is_custom_code = True
                    continue
                if d and is_custom_code:
                    tag_name = ""
                    is_custom_code = False
                    continue
                if is_custom_code:
                    model_tags[tag_name] += line

        
        models_texts = ""
        models_imports = []
        models = Model.objects.filter(application=self)
        for model in models:
            model_tags.setdefault("@%s@" % (model,), "")
            if not first_time: 
                model_text, imports = model.generate_model(custom_code = model_tags["@%s@" % (model,)])
                models_texts += model_text
                models_imports.extend(imports)
        models_file = "from django.db import models\n\n"
        for model_import in models_imports:
            models_file += model_import
        models_file += "# <-- User custom imports after this tag. Do not remove!!!\n"
        if not first_time: models_file += model_tags["custom_imports"]
        models_file += "# End of user custom imports. Do not remove this tag!!! -->\n\n\n"
        models_file += models_texts
        f = open(models_file_path, 'w')
        f.write(models_file)
        f.close()

    def save(self):
        if self.id is None:
            self.start_app()
            self.generate_models(first_time=True)
        super(Application, self).save()

class Model(models.Model):
    name = models.CharField(max_length=200)
    application = models.ForeignKey(Application)

    def __unicode__(self):
        return self.name

    def generate_model(self, custom_code=""):
        fields = FieldOption.objects.filter(model=self)
        imports = []
        model = "class %s(models.Model):\n" % self.name
        if not fields:
            model += "    pass\n"
        for field in fields:
            field_text = "    %s\n" % (field)
            my_name = "%s.%s" % (self.application.name, self.name,)
            if my_name in field_text:
                model += field_text.replace(my_name, "'self'")
            else:
                if self.application.name in field_text:
                    model += field_text.replace("%s." % (self.application,), "")
                else:
                    imports_for_foreign = field.field.get_import_for_foreign()
                    if imports_for_foreign: 
                        imports.append("from %s import %s\n" % imports_for_foreign)
                        model += field_text.replace("%s." % imports_for_foreign[0], "")
                    else:
                        model += field_text

        model += "\n\n"
        model += "    # <-- @%s@ User generated code after this tag. Do not remove!!!\n" % (self.name,)
        model += custom_code
        model += "    # @%s@ End of user generated code. Do not remove this tag!!! -->\n" % (self.name,)

        return model, imports

    def save(self):
        super(Model, self).save()
        self.application.generate_models()

    def delete(self):
        my_application = self.application
        options = Option.objects.filter(value=str(self.id), option_type__is_model=True)
        fields_to_delete = []
        applications_to_regenerate = []
        for option in options:
            fields_to_delete.append(option.field)
        for field in fields_to_delete:
            field_options = FieldOption.objects.filter(field=field)
            for field_option in field_options: 
                applications_to_regenerate.append(field_option.model.application)
        super(Model, self).delete()
        for field in fields_to_delete:
            field.delete()
        for app in set(applications_to_regenerate):
            app.generate_models()
        my_application.generate_models()

class OptionType(models.Model):
    name = models.CharField(max_length=200)
    is_boolean = models.BooleanField()
    is_text = models.BooleanField()
    is_numeric = models.BooleanField()
    is_big_text = models.BooleanField()
    no_key_value = models.BooleanField()
    is_model = models.BooleanField()

    def __unicode__(self):
        if self.is_boolean:
            type="boolean"
        if self.is_text or self.is_numeric:
            type="text"
        if self.is_big_text:
            type="big_text"
        if self.is_model:
            type="model"

        return "%s**%s" % (self.name, type)

class FieldType(models.Model):
    name = models.CharField(max_length=200)
    has_no_readable_name = models.BooleanField()
    option_types = models.ManyToManyField(OptionType, blank=True)
    required_option_types = models.ManyToManyField(OptionType, blank=True, related_name="required_fieldtype_optiontype")

    def __unicode__(self):
        return self.name

class Field(models.Model):
    name = models.CharField(max_length=200)
    human_readable_name = models.CharField(max_length=200, blank=True)
    type = models.ForeignKey(FieldType)

    def __unicode__(self):
        return self.name
    
    def get_import_for_foreign(self):
        opts = Option.objects.filter(field=self)
        options = ""
        for opt in opts:
            if opt.option_type.is_model:
                model = Model.objects.get(id=opt.value)
                return (model.application, model.name)
        return ""

    def get_structure(self):
        opts = Option.objects.filter(field=self)
        options = ""
        for opt in opts:
            if opt.option_type.is_big_text or opt.option_type.is_text:
                options += "%s='%s', " % (opt.option_type.name, opt.value)
            if opt.option_type.is_boolean or opt.option_type.is_numeric:
                options += "%s=%s, " % (opt.option_type.name, opt.value)
            if opt.option_type.no_key_value:
                value = opt.value
                if opt.option_type.is_model:
                    model = Model.objects.get(id=value)
                    value = "%s.%s" % (model.application, model.name)
                options += "%s, " % (value,)

        human_readable_name=""
        if self.human_readable_name and not self.type.has_no_readable_name:
             human_readable_name = "'%s', " % self.human_readable_name
        options = human_readable_name + options
        options = options[0:-2]
        return "%s = %s(%s)" % (self.name, self.type.name, options)
 
class Option(models.Model):
    field = models.ForeignKey(Field)
    option_type = models.ForeignKey(OptionType)
    value = models.CharField(max_length=200, blank=True)

    def __unicode__(self):
        return "%s=%s" % (self.option_type.name, self.value)

class FieldOption(models.Model):
    field = models.ForeignKey(Field)
    model = models.ForeignKey(Model)
    
    def __unicode__(self):
        return self.field.get_structure()
    
    def save(self):
        super(FieldOption, self).save()
        self.model.application.generate_models()
        


# Create your models here.
